package com.ghp.admin.api;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ghp.admin.service.UserInterfaceInfoService;
import com.ghp.common.api.InnerUserInterfaceInfoService;
import com.ghp.common.exception.BusinessException;
import com.ghp.common.model.entity.UserInterfaceInfo;
import com.ghp.common.utils.ThrowUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Collections;
import java.util.Objects;

import static com.ghp.common.constants.ErrorMessageConstants.*;
import static com.ghp.common.constants.MessageQueueConstants.QUEUE_INTERFACE_CALL_NUMBER;
import static com.ghp.common.constants.RedisConstants.INTERFACE_LEFT_NUM_KEY;

/**
 * 内部用户接口关系服务，提供内部模块之间的接口
 *
 * @author ghp
 */
@DubboService
@Slf4j
public class InnerUserInterfaceInfoServiceImpl implements InnerUserInterfaceInfoService {

    @Resource
    private UserInterfaceInfoService userInterfaceInfoService;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private RabbitTemplate rabbitTemplate;

    /**
     * Redis脚本，用于扣减接口调用次数
     */
    private static final DefaultRedisScript<Long> DECREMENT_SCRIPT;

    static {
        DECREMENT_SCRIPT = new DefaultRedisScript<>();
        DECREMENT_SCRIPT.setLocation(new ClassPathResource("lua/decrement-leftnum.lua"));
        DECREMENT_SCRIPT.setResultType(Long.class);
    }

    /**
     * 统计接口调用次数
     *
     * @param interfaceInfoId 接口ID
     * @param userId          用户ID
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean invokeCount(long interfaceInfoId, long userId) {
        String hashKey = userId + "-" + interfaceInfoId;
        // 判断接口剩余调用次数是否充足
        Integer interfaceLeftNum = this.getInterfaceLeftNum(hashKey);
        ThrowUtils.throwIf(Objects.isNull(interfaceLeftNum) || interfaceLeftNum <= 0, INTERFACE_NOT_EXISTS);
        // 扣减接口剩余调用次数
        Long result = null;
        try {
            result = (Long) redisTemplate.execute(
                    DECREMENT_SCRIPT,
                    Arrays.asList(INTERFACE_LEFT_NUM_KEY, hashKey),
                    Collections.emptyList());
        } catch (Exception e) {
            throw new BusinessException(SCRIPT_EXECUTE_ERROR);
        }
        ThrowUtils.throwIf(result == null || result.intValue() < 0, INTERFACE_DECREMENT_FAIL);
        // 往队列中添加一个任务，异步更新数据库
        rabbitTemplate.convertAndSend(QUEUE_INTERFACE_CALL_NUMBER, hashKey);
        return true;
    }

    /**
     * 获取接口剩余调用次数
     *
     * @param hashKey
     * @return
     */
    private Integer getInterfaceLeftNum(String hashKey) {
        ThrowUtils.throwIf(StringUtils.isBlank(hashKey), PARAMS_NOT_NULL);
        return (Integer) redisTemplate.opsForHash().get(INTERFACE_LEFT_NUM_KEY, hashKey);
    }

    /**
     * 检查接口是否有剩余调用次数
     */
    @Override
    public UserInterfaceInfo checkLeftNum(String userIdAndInterfaceId) {
        Long[] userIdAndInterfaceInfoId = getUserIdAndInterfaceInfoId(userIdAndInterfaceId);
        Long userId = userIdAndInterfaceInfoId[0];
        Long interfaceInfoId = userIdAndInterfaceInfoId[1];
        UserInterfaceInfo userInterfaceInfo = userInterfaceInfoService.getOne(
                new LambdaQueryWrapper<UserInterfaceInfo>()
                        .eq(UserInterfaceInfo::getInterfaceInfoId, interfaceInfoId)
                        .eq(UserInterfaceInfo::getUserId, userId));
        return userInterfaceInfo;
    }

    /**
     * 解析出 userId 和 interfaceInfoId
     *
     * @param userIdAndInterfaceId 格式为 userId-interfaceInfoId
     * @return
     */
    private Long[] getUserIdAndInterfaceInfoId(String userIdAndInterfaceId) {
        int index = userIdAndInterfaceId.indexOf("-");
        Long userId = Long.valueOf(userIdAndInterfaceId.substring(0, index));
        Long interfaceInfoId = Long.valueOf(userIdAndInterfaceId.substring(index + 1));
        return new Long[]{userId, interfaceInfoId};
    }
}




